using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics.Contracts;
using System.Reflection;

namespace OpenWaves.ActivityFeed
{
    [TypeConverter(typeof(UserGroupConverter))]
    public class UserGroup : IEquatable<UserGroup>
    {        
        public string Name { get; private set; }

        protected UserGroup()
        {
            
        }

        public UserGroup(string name)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(name) == false);
            this.Name = name.ToUpperInvariant();
        }

        public override string ToString()
        {
            return this.Name;
        }

        public static UserGroup FromUserId(string userId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(userId) == false);

            return new UserGroup("user/" + userId);
        }

        public static UserGroup FromRole(string role)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(role) == false);
            return new UserGroup("role/" + role);
        }

        public bool Equals(UserGroup other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.Name, this.Name);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (UserGroup)) return false;
            return this.Equals((UserGroup) obj);
        }

        public override int GetHashCode()
        {
            return (this.Name != null ? this.Name.GetHashCode() : 0);
        }

        public static bool operator ==(UserGroup left, UserGroup right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(UserGroup left, UserGroup right)
        {
            return !Equals(left, right);
        }
    }

    public class UserGroupConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            var str = value as string;
            return str == null ? base.ConvertFrom(context, culture, value) : new UserGroup(str);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return (destinationType == typeof(string)) || (destinationType == typeof(InstanceDescriptor)) ||
                   base.CanConvertTo(context, destinationType);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            var userGroup = value as UserGroup;
            if (userGroup == null)
            {
                throw new ArgumentException("Invalid UserGroup", "value");
            }

            if (destinationType == typeof(string))
            {
                return (value ?? "").ToString();
            }

            // convert to a instance descriptor
            if (destinationType == typeof(InstanceDescriptor))
            {
                // used to describe the constructor
                MemberInfo member = null;
                object[] arguments = null;

                // get the constructor of our Latitude type
                member = typeof(UserGroup).GetConstructor(new Type[] { typeof(string) });

                // the arguments to pass along to the Latitude constructor
                arguments = new object[] { userGroup.ToString() };

                // return instance descriptor or null if we could not 
                // find a constructor
                if (member != null)
                    return new InstanceDescriptor(member, arguments);
                else
                    return null;
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}